


/**
 * 与服务器指令交互的处理实现类
 *
 * @author lizb
 */
public class CommandExecutorImpl {
    private static final String SIGN_OK = "OK";
    private static final String POSTFIX = ">";
    private static final String ERROR = "ERROR";
    private static final int MAX_PORT = 65536;
    private static final Logger log = LoggerFactory.getLogger(CommandExecutorImpl.class);

    private final JobManager jobManager = JobManager.getInstance();

    @Override
    public void onSpecialTimeOperated(Connection conn, String detailCmd) {
        String prefix = "<A_";
        Matcher matcher;
        String regexSkip = "^\\[[0-9]{6}skip]$";
        Pattern skipPattern = Pattern.compile(regexSkip);
        matcher = skipPattern.matcher(detailCmd);
        if (matcher.matches()) {
            OfflineCommandManager.aCommandReceive();
            sendOK(conn, prefix);
            return;
        }
//		使用非捕获分组方式 当离线指令过长的时候会导致堆栈溢出 todo
//		出现堆栈溢出 无法使用正则匹配
        if(true) {
            OfflineCommandManager.aCommandReceive();
            try {
                OfflineCommandManager.saveOfflineCommand(detailCmd);
                sendOK(conn, prefix);
            } catch (Exception e) {
                sendError(conn, prefix);
                log.error(LoggerUtil.convertException2String(e));
            }
            jobManager.cancelOfflineCommandJob();
            JobRunner.getInstance().sendOfflineCommandOfToday();
            JobRunner.getInstance().refreshOfflineCommand();
        } else {
            sendError(conn, prefix);
            log.trace("Command Format Error");
        }
    }

    /**
     * b指令由设备发送给服务器 没有实现方法
     * @param conn 当前连接
     * @param detailCmd 指令内容
     */
    @Override
    public void onLocalStorageData(Connection conn, String detailCmd) {
        String prefix = "<B_";
    }

    @Override
    public void onWriteBackupServerPort(Connection conn, String detailCmd) {
        String prefix = "<C_";
        String cleanCommand = "clean";
        //清除ip
        if (cleanCommand.equals(detailCmd)) {
            try {
                PropertiesUtil.setPropertyOrReplaceDefault(Consts.KEY_BACKUP_IP_ADDR, "");
                PropertiesUtil.setPropertyOrReplaceDefault(Consts.KEY_BACKUP_IP_PORT, "");
                sendOK(conn, prefix, cleanCommand);
            } catch (IOException e) {
                log.error(LoggerUtil.convertException2String(e));
                sendError(conn, prefix, cleanCommand);
            }
            return;
        }
        //写入备选ip
        String[] spilt = detailCmd.split("_");
        int splitLength = 2;
        if (spilt.length == splitLength) {
            //ip匹配正则
            String ipRegex = "^((?:(?:25[0-5]|2[0-4]\\d|(?:1\\d{2}|[1-9]?\\d))\\.){3}(?:25[0-5]|2[0-4]\\d|(?:1\\d{2}|[1-9]?\\d)))$";
            //端口号匹配，暂时用parseInt
            String portRegex = "^([1-9]|[1-9]\\d{3}|[1-6][0-5][0-5][0-3][0-5])$";
            Pattern pattern = Pattern.compile(ipRegex);
            Matcher matcher;
            matcher = pattern.matcher(spilt[0]);
            if (matcher.matches()) {
                try {
                    int port = Integer.parseInt(spilt[1]);
                    if (port > 0 && port <= MAX_PORT) {
                        PropertiesUtil.setPropertyOrReplaceDefault(Consts.KEY_BACKUP_IP_ADDR, spilt[0]);
                        PropertiesUtil.setPropertyOrReplaceDefault(Consts.KEY_BACKUP_IP_PORT, spilt[1]);
                        sendOK(conn, prefix, detailCmd);
                    } else {
                        log.trace("Command C:port number out of range");
                        sendError(conn, prefix);
                    }
                } catch (Exception e) {
                    log.error(LoggerUtil.convertException2String(e));
                    sendError(conn, prefix);
                }
            } else {
                log.trace("Command C:SERVER_IP dont match");
                sendError(conn, prefix);
            }
        } else {
            sendError(conn, prefix);
        }
    }

    @Override
    public void onDeleteLocalSwitch(Connection conn, String detailCmd) {
        String prefix = "<D_";
        if (OfflineCommandManager.deleteAllOfflineCommand()) {
            jobManager.cancelOfflineCommandJob();
            sendOK(conn, prefix);
            new Thread(()-> {
                OfflineCommandManager.requestForOfflineCommand(conn);

            }).start();
        } else {
            log.trace("Command D:DeleteLocal offlineCommand is fail");
            sendError(conn, prefix);
        }
    }

    @Override
    public void onTodayTimer(Connection conn, String detailCmd) {
        final TodayCommandManager todayCommandManager = TodayCommandManager.getInstance();
        String prefix = "<E_";
        if (detailCmd == null) {
            sendOK(conn, prefix, todayCommandManager.getTodayTimer());
        } else {
            String[] split = detailCmd.split("_");
            String clean = "clean";
            if (split.length == 1 && clean.equals(split[0])) {
                if (todayCommandManager.cleanTodayTimer()) {
                    sendOK(conn, prefix, clean);
                } else {
                    log.trace("Command E:todayTimer clean fail");
                    sendError(conn, prefix, clean);
                }
            } else if (split.length == 3) {
                if (todayCommandManager.setTodayTimerCommand(split[0], split[1], split[2])) {
                    sendOK(conn, prefix, detailCmd);
                } else {
                    sendError(conn, prefix);
                }
            } else {
                log.trace("Command E:todayTimer not match");
                sendError(conn, prefix);
            }
        }
    }

    @Override
    public void onNewRequestLocation(Connection conn, String detailCmd) {
        String prefix = "<F_";
        if (detailCmd != null && !detailCmd.isEmpty()) {
            sendError(conn, prefix);
            return;
        }
        try {
            IGPSDevice gpsSensor = (IGPSDevice) ExtendDeviceManager.getInstance().getExtendDevice(ExtendDeviceEnum.GPS);
            if (gpsSensor != null) {
                String location = gpsSensor.updateGpsInformation().returnForFCommand();
                String regexLocation = "(?:[0-9]*\\.?[0-9]*[enwsENWS])_(?:[0-9]*\\.?[0-9]*[enwsENWS])";
                if (Pattern.matches(regexLocation,location)) {
                    String res = location.toLowerCase();
                    sendOK(conn, prefix, res);
                } else {
                    log.trace("Command F: location format error");
                    sendError(conn, prefix);
                }
            } else {
                log.trace("Command F: cannot get device location");
                sendError(conn, prefix);
            }
        } catch (Exception e) {
            log.error(LoggerUtil.convertException2String(e));
            sendError(conn, prefix);
        }
    }

    @Override
    public void onRequestLocation(Connection conn, String detailCmd) {
        String prefix = "<G_";
        if (detailCmd != null && !detailCmd.isEmpty()) {
            sendError(conn, prefix);
            return;
        }
        try {
            IGPSDevice gpsSensor = (IGPSDevice) ExtendDeviceManager.getInstance().getExtendDevice(ExtendDeviceEnum.GPS);
            String location = gpsSensor.updateGpsInformation().returnForFCommand();
            if (location != null) {
                String regexLocation = "(?:^[0-9]*\\.?[0-9]*[enwsENWS]$),(?:^[0-9]*\\.?[0-9]*[enwsENWS]$)";
                if (Pattern.matches(regexLocation,location)) {
                    String res = regexLocation.toLowerCase();
                    sendOK(conn, prefix, res);
                } else {
                    log.trace("Command G: location format error");
                    sendError(conn, prefix);
                }
            } else {
                log.trace("Command G: cannot get device location");
                sendError(conn, prefix);
            }
        } catch (Exception e) {
            log.error(LoggerUtil.convertException2String(e));
            sendError(conn, prefix);
        }
    }

    @Override
    public void onWriteTime(Connection conn, String detailCmd) {
        String prefix = "<H_";
        SimpleDateFormat sdf = new SimpleDateFormat(Consts.TIMESET_FORMAT);
        try {
            CmdUtil.setDate(sdf.parse(detailCmd));
            sendOK(conn, prefix, detailCmd);
        } catch (Exception e) {
            log.error(LoggerUtil.convertException2String(e));
            sendError(conn, prefix);
        }
    }

    @Override
    public void onReadTime(Connection conn, String detailCmd) {
        String prefix = "<I_";
        if (detailCmd != null && !detailCmd.isEmpty()) {
            sendError(conn, prefix);
            return;
        }
        Calendar calendar = Calendar.getInstance();
        Date date = calendar.getTime();
        SimpleDateFormat sdf = new SimpleDateFormat(Consts.TIMESET_FORMAT);
        String dateStr = sdf.format(date);
        sendOK(conn, prefix, dateStr);
    }

    @Override
    public void onCheckSwitch(Connection conn, String detailCmd) {
        String prefix = "<J_";
        if (detailCmd == null) {
            int[] status = LoopManager.getInstance().getEighthLoopStatus();
            String msg = StringUtil.commandInt2String(status);
            if (!msg.isEmpty()) {
                sendOK(conn, prefix, msg);
            } else {
                sendError(conn, prefix);
            }
        } else {
            sendError(conn, prefix);
        }
    }

    private String lastCommand;
    @Override
    public void onChannelOperated(Connection conn, String detailCmd) {
        //long startTime = System.currentTimeMillis();
        String prefix = "<K_";
        try {
            if (StringUtil.isNullOrEmpty(detailCmd)) {
                sendError(conn, prefix);
                return;
            }
            String[] split = detailCmd.split("_");
            String command = split[0];
            //视频播放调节节目
            IExtendDevice videoDevice = ExtendDeviceManager.getInstance().getExtendDevice(ExtendDeviceEnum.VIDEO);
            if(videoDevice != null) {
                Matcher matcher = VideoDevice.videoExpression.matcher(detailCmd);
                if(matcher.matches()) {
                    String video = Integer.valueOf(Integer.parseInt(matcher.group(2),2)).toString();//指定播放节目
                    String speed = Integer.valueOf(Integer.parseInt(matcher.group(3),2)).toString();//播放速度
                    String mode = Integer.valueOf(Integer.parseInt(matcher.group(4),2)).toString();//播放模式
                    String bright = Integer.valueOf(Integer.parseInt(matcher.group(5),2)).toString();//亮度
                    //String times = matcher.group(6);//播放时长

                    //给视频服务器类赋值
                    VideoServer vs = VideoServer.getInstance();
                    if((!vs.getVideo().equals(video) || !vs.getSpeed().equals(speed) || !vs.getMode().equals(mode) || !vs.getBright().equals(bright)) &&
                            (Integer.valueOf(speed)>=1 && Integer.valueOf(speed)<=8 &&  Integer.valueOf(mode)>=0 && Integer.valueOf(mode)<=4)) {
                        vs.setMode(mode);
                        vs.setVideo(video);
                        vs.setSpeed(speed);
                        vs.setBright(bright);
                        //vs.setTimes(times);

                        new VideoDevice(videoDevice.getExtendNum());
                    }
                }
            }
            int loopNum = LoopManager.getInstance().getLoopNum();
            if (PropertiesUtil.getPropertiesDefault("hangzhoushow","").equals("true")) {
                if (!KCommandExecutor.hangZhouShow(command)) {
                    return;
                }
            }
            //保持k指令 无论正确与否 因为可能回路设备还没有被识别到 如果小于回路设备数量 直接追加0
            if (command.length() > loopNum) {
//				command = command.substring(0, loopNum);
                log.error("the channel size is not same in device and server. server channel size ="
                          + split[0].length() + " device channel size=" + loopNum);
            } else if(command.length() < loopNum) {
                StringBuilder sbCom = new StringBuilder(command);
                while (sbCom.length() < loopNum) {
                    sbCom.append(0);
                }
                command = sbCom.toString();
                log.error("the channel size is not same in device and server. server channel size ="
                          + split[0].length() + " device channel size=" + loopNum);
            }
            //k指令的两种格式带有效势时长跟不带有效时常
            int kWithTimeSplitLength = 2;
            if (split.length == kWithTimeSplitLength) {
                long time = Long.valueOf(split[1]) * 1000;
                if (KCommandManager.getInstance().saveKCommandWithTime(command, time)) {
                    sendOK(conn, prefix, command + "_" + split[1]);
                } else {
                    sendError(conn, prefix);
                }
            } else {
                if (KCommandManager.getInstance().saveKCommand(command)) {
                    sendOK(conn, prefix, command);
                } else {
                    sendError(conn, prefix);
                }
            }
        } catch (Exception e1) {
            sendError(conn, prefix);
            log.error(LoggerUtil.convertException2String(e1));
        }
    }

    @Override
    public void onChannel(Connection conn, String detailCmd) {
        String prefix = "<L_";
        if (detailCmd == null) {
            int num = LoopManager.getInstance().getLoopNum();
            if (num > 0) {
                StringBuilder sbNum = new StringBuilder().append(num);
                while (sbNum.length() < Consts.DEFAULT_LOOP_NUM_LENGTH) {
                    sbNum.insert(0, "0");
                }
                sendOK(conn, prefix, sbNum.toString());
                return;
            }
        }
        sendError(conn, prefix);
    }

    @Override
    public void onOperatedState(Connection conn, String detailCmd) {
        String prefix = "<M_";
        if (LoopManager.getInstance().returnToAutoMode()) {
            sendOK(conn, prefix);
        } else {
            sendError(conn, prefix);
        }
    }

    /**
     * 暂时没有外界的回路电压电流模块需要上报
     * @param conn 当前连接
     * @param detailCmd 指令内容
     */
    @Override
    public void onExtendCurrentAndVoltage(Connection conn, String detailCmd) {
        String prefix = "<N_";
        sendError(conn, prefix);
    }

    @Override
    public void onDoorOpenState(Connection conn, String detailCmd) {
        String prefix = "<O_";
        if (ExtendDeviceManager.getInstance().containsExtendDevice(ExtendDeviceEnum.DOOR_ALARM)) {
            try {
                IDoorAlarmDevice doorOpenAlarmDevice = (IDoorAlarmDevice) ExtendDeviceManager.getInstance().getExtendDevice(ExtendDeviceEnum.DOOR_ALARM);
                sendOK(conn, prefix, doorOpenAlarmDevice.returnForOCommand());
            } catch (Exception e) {
                sendError(conn, prefix, "door");
            }
        } else {
            sendError(conn, prefix, "door");
        }
    }

    @Override
    public void onSunLight(Connection conn, String detailCmd) {
        String prefix = "<P_";
        ILightSensationDevice gpsSensor = (ILightSensationDevice) ExtendDeviceManager.getInstance().getExtendDevice(ExtendDeviceEnum.GPS);
        if (gpsSensor != null) {
            String lightIntensity = gpsSensor.updateLightSensationInformation().returnForPCommand();
            if (detailCmd == null) {
                sendOK(conn, prefix, lightIntensity);
            } else {
                String[] cmdSplit = detailCmd.split("_");
                int pCommandWithParameterSplitLength = 3;
                if (cmdSplit.length == pCommandWithParameterSplitLength) {
                    try {
                        LightIntensityController.getInstance().saveLightIntensity(cmdSplit[0], MathUtil.HexStringToBinaryString(cmdSplit[1]), String.valueOf(Long.valueOf(cmdSplit[2])));
                        sendOK(conn, prefix, lightIntensity + "_" + detailCmd);
                    } catch (ClassCastException e) {
                        sendError(conn, prefix);
                        log.error(LoggerUtil.convertException2String(e));
                    }
                } else {
                    sendError(conn, prefix);
                }
            }
        } else {
            sendError(conn, prefix);
        }
    }

    @Override
    public void onNetworkState(Connection conn, String detailCmd) {
        String prefix = "<Q_";
        try {
            String netSignalStrength = NetworkManager.getInstance().getNetSignalStrength();
            sendOK(conn, prefix, netSignalStrength);
        } catch (Exception e) {
            sendError(conn,prefix);
        }
    }

    @Override
    public void onTemperature(Connection conn, String detailCmd) {
        String prefix = "<T_";
        StringBuilder sb = new StringBuilder();
        DecimalFormat df = new DecimalFormat("000.00");
        SystemInnerTemperatureSensor systemInnerTemperatureSensor = SystemInnerTemperatureSensor.getInstance();
        double currTmp = systemInnerTemperatureSensor.getCurrTmp();
        String fansState = "00";
        String format = df.format(currTmp);
        if (currTmp == -1) {
            sendError(conn, prefix);
            return;
        }
        if (detailCmd == null) {
            if (currTmp < 0) {
                sb.append(format).append("_").append(systemInnerTemperatureSensor.getTmpThresholdLow()).append("_").append(fansState);
            } else {
                sb.append("+").append(format).append("_").append(systemInnerTemperatureSensor.getTmpThresholdLow() ).append("_").append(fansState);
            }
        } else {
            try {
                int tmpThresholdLow;
                tmpThresholdLow = Integer.parseInt(detailCmd);
                systemInnerTemperatureSensor.setTmpThreshold(tmpThresholdLow);
                if (currTmp < 0) {
                    sb.append(format).append("_").append(detailCmd).append("_").append(fansState);
                } else {
                    sb.append("+").append(format).append("_").append(detailCmd).append("_").append(fansState);
                }
            } catch (Exception e) {
                sendError(conn, prefix);
                log.error(LoggerUtil.convertException2String(e));
                return;
            }
        }
        sendOK(conn, prefix, sb.toString());
    }

    @Override
    public void onCurrentAndVoltage(Connection conn, String detailCmd) {
        String prefix = "<U_";
        if (detailCmd == null) {
            String msg = VoltageAndCurrentManager.getInstance().getFormattedAllCurrentAndVoltageString();
            if (!StringUtil.isNullOrEmpty(msg) && !Consts.ERROR_CODE.equals(msg)) {
                sendOK(conn, prefix, msg);
                return;
            }
        }
        sendError(conn, prefix);
    }

    @Override
    public void onVersion(Connection conn, String detailCmd) {
        String prefix = "<V_";
        String version = ClientConfig.VERSION;
        String deviceVersion = IniUtils.getIniByKey("VERSION","SYSTEM", ClientConfig.SYSTEM_INI_PATH);
        if(deviceVersion != null) {
            deviceVersion = StringUtil.checkStringNotEmptyAndNull(deviceVersion, "0");
            version += "_"+deviceVersion;
        }
        if (!StringUtil.isNullOrEmpty(version)) {
            sendOK(conn, prefix, version);
        } else {
            sendError(conn, prefix);
        }
    }

    @Override
    public void onPower(Connection conn, String detailCmd) {
        String prefix = "<W_";
        if (detailCmd == null && ExtendDeviceManager.getInstance().containsExtendDevice(ExtendDeviceEnum.POWER)) {
            IPowerDevice powerRemoteReader = (IPowerDevice) ExtendDeviceManager.getInstance().getExtendDevice(ExtendDeviceEnum.POWER);
            String msg = null;
            try {
                msg = powerRemoteReader.updatePowerInformation().returnForWCommand();
            } catch (Exception e) {
                sendError(conn, prefix);
                return;
            }
            if (!StringUtil.isNullOrEmpty(msg) && !Consts.ERROR_CODE.equals(msg)) {
                sendOK(conn, prefix, msg);
                return;
            }
        }
        sendError(conn, prefix);
    }

    @Override
    public void onRequestDeviceId(Connection conn, String detailCmd) {
        UUID deviceId;
        String prefix = "<X_";
        if (detailCmd != null) {
            sendError(conn, prefix);
            return;
        }
        deviceId = Connection.getInstance().getDeviceId();
        if (deviceId == null) {
            sendError(conn, prefix);
        } else {
            sendOK(conn, prefix, StringUtil.uuid2Str(deviceId));
        }
//		OfflineCommandManager.requestForOfflineCommand(conn);
    }

    @Override
    public void onHumidity(Connection conn, String detailCmd) {
        String prefix = "<Y_";
        DecimalFormat df = new DecimalFormat("000.00");
        double humidity = 0;
        if (ExtendDeviceManager.getInstance().containsExtendDevice(ExtendDeviceEnum.TMP)) {
            ITemperatureAndHumidityDevice sensor = (ITemperatureAndHumidityDevice) ExtendDeviceManager.getInstance().getExtendDevice(ExtendDeviceEnum.TMP);
            humidity = sensor.updateHumidityInformation().getHumidity();
            String formatHumidity = df.format(humidity);
            sendOK(conn, prefix, String.valueOf(formatHumidity));
        } else {
            sendError(conn, prefix);
            return;
        }
    }

    @Override
    public void simulatorKeyPress(Connection conn, String detailCmd) {
        String prefix = "<Z_";
        try {
            String msg = KeyPad.getInstance().simulatorKeyPress(detailCmd);

            sendOK(conn, prefix);
        } catch (Exception e) {
            log.error(LoggerUtil.convertException2String(e));
            sendError(conn, prefix);
        }
    }

    @Override
    public void onPingBack(Connection conn, String detailCmd) {
        /*String prefix = "<Z_";
        try {
        	String msg = KeyPad.getInstance().simulatorKeyPress(detailCmd);

        	sendOK(conn, prefix);
        } catch (Exception e) {
        	log.error(LoggerUtil.convertException2String(e));
        	sendError(conn, prefix);
        }*/
        log.info("ping time is " + (System.currentTimeMillis() - Long.parseLong(detailCmd)));

    }
    /**
     * 发送失败
     *
     * @param conn 当前链接
     * @param prefix 失败的协议的前缀
     */
    private void sendError(Connection conn, String prefix) {
        StringBuilder sb = new StringBuilder();
        conn.appendMsgToQueue(sb.append(prefix).append(ERROR).append(POSTFIX).toString());
        saveSendMsg(sb.deleteCharAt(0).deleteCharAt(sb.length() - 1).toString());
    }

    private void sendError(Connection conn, String prefix, String msg) {
        StringBuilder sb = new StringBuilder();
        conn.appendMsgToQueue(sb.append(prefix).append(msg).append("_").append(ERROR).append(POSTFIX).toString());
        saveSendMsg(sb.deleteCharAt(0).deleteCharAt(sb.length() - 1).toString());
    }

    /**、
     * 发送成功
     *
     * @param conn 当前链接
     * @param prefix 成功的协议的前缀
     * @param msg 发送的消息
     */
    private void sendOK(Connection conn, String prefix, String msg) {
        StringBuilder sb = new StringBuilder();
        conn.appendMsgToQueue(sb.append(prefix).append(msg).append("_").append(SIGN_OK).append(POSTFIX).toString());
        log.debug(sb.toString());
        saveSendMsg(sb.deleteCharAt(0).deleteCharAt(sb.length() - 1).toString());
    }

    private void sendOK(Connection conn, String prefix) {
        StringBuilder sb = new StringBuilder();
        conn.appendMsgToQueue(sb.append(prefix).append(SIGN_OK).append(POSTFIX).toString());
        saveSendMsg(sb.deleteCharAt(0).deleteCharAt(sb.length() - 1).toString());
    }

    /**
     * 推送消息格式<prefix_msg>
     * @param conn  当前链接
     * @param prefix    命令前缀
     * @param msg   发送的消息
     */
    private void send(Connection conn, String prefix, String msg) {
        StringBuilder sb = new StringBuilder();
        conn.appendMsgToQueue(sb.append(prefix).append(msg).append(POSTFIX).toString());
        saveSendMsg(sb.deleteCharAt(0).deleteCharAt(sb.length() - 1).toString());
    }

    /**
     * 将当前的交互指令存下来并通知交互指令页面去刷新页面
     *
     * @param msg 发送的消息
     */
    private void saveSendMsg(String msg) {
        try {
            PropertiesUtil.setPropertyOrReplaceDefault(Consts.KEY_INTERACTION_SEND_MSG, msg);
        } catch (IOException e) {
            log.error(LoggerUtil.convertException2String(e));
        }
        InteractionCommandExecutor.getInstance().notifyListener();
    }
}
